﻿using Telerik.Web.UI;
using System.Collections.Generic;
using Models;
using System;
using System.Data.Objects;
using System.Data.Common;
using System.Data.SqlClient;
namespace Provider
{
    public class PatientSchedulerProviderBase : DbSchedulerProviderBase
    {
        public IDictionary<int, Resource> _doctorAppointments;
        public IDictionary<int, Resource> _patients;

        private IDictionary<int, Resource> DoctorAppointments
        {
            get
            {
                return _doctorAppointments;
            }
        }

        private IDictionary<int, Resource> Patients
        {
            get
            {
                return _patients;
            }
        }

        private void LoadResources()
        {
            if (_doctorAppointments == null)
            {
                _doctorAppointments = new Dictionary<int, Resource>();
            }
            _doctorAppointments.Clear();
            foreach (Resource doctorAppointment in LoadDoctorAppointments())
            {
                _doctorAppointments.Add((int)doctorAppointment.Key, doctorAppointment);
            }
            if (_patients == null)
            {
                _patients = new Dictionary<int, Resource>();
            }
            _patients.Clear();
            foreach (Resource patient in LoadPatients())
            {
                _patients.Add((int)patient.Key, patient);
            }
        }

        public override IEnumerable<Appointment> GetAppointments(RadScheduler owner)
        {
            List<Appointment> appointments = new List<Appointment>();

            DatabaseEntities databaseEntities = new DatabaseEntities();
            foreach (PatientAppointment patientAppointment in databaseEntities.PatientAppointments)
            {
                Appointment apt = new Appointment();
                apt.Owner = owner;
                apt.ID = patientAppointment.Id; ;
                apt.Subject = string.Format("{0} {1}", patientAppointment.Patient.FirstName, patientAppointment.Patient.LastName);
                apt.Start = DateTime.SpecifyKind(Convert.ToDateTime(patientAppointment.Start), DateTimeKind.Utc);
                apt.End = DateTime.SpecifyKind(Convert.ToDateTime(patientAppointment.End), DateTimeKind.Utc);
                if (apt.RecurrenceParentID != null)
                {
                    apt.RecurrenceState = RecurrenceState.Exception;
                }
                else if (apt.RecurrenceRule != string.Empty)
                {
                    apt.RecurrenceState = RecurrenceState.Master;
                }
                LoadResources(apt);
                appointments.Add(apt);
            }
            return appointments;
        }

        public override void Insert(RadScheduler owner, Appointment appointmentToInsert)
        {
            if (!PersistChanges)
            {
                return;
            }
            using (DbConnection conn = OpenConnection())
            {
                using (DbTransaction tran = conn.BeginTransaction())
                {
                    DbCommand cmd = DbFactory.CreateCommand();
                    cmd.Connection = conn;
                    cmd.Transaction = tran;
                    PopulateAppointmentParameters(cmd, appointmentToInsert);
                    cmd.CommandText =
                         @" INSERT INTO [PatientAppointment]
                           ([Subject], [Start], [End], [PatientId],[DoctorAppointmentId])
                    VALUES (@Subject, @Start, @End, @PatientId,@DoctorAppointmentId)";
                    tran.Commit();
                }
            }
        }

        public override void Update(RadScheduler owner, Appointment appointmentToUpdate)
        {
            if (!PersistChanges)
            {
                return;
            }
            using (DbConnection conn = OpenConnection())
            {
                using (DbTransaction tran = conn.BeginTransaction())
                {
                    DbCommand cmd = DbFactory.CreateCommand();
                    cmd.Connection = conn;
                    cmd.Transaction = tran;
                    PopulateAppointmentParameters(cmd, appointmentToUpdate);
                    cmd.Parameters.Add(CreateParameter("@id", appointmentToUpdate.ID));
                    cmd.CommandText = "UPDATE [PatientAppointment] SET [Subject] = @Subject, [Start] = @Start, [End] = @End, [DoctorAppointmentID] = @DoctorAppointmentID, [PatientID] = @PatientID WHERE [Id] = @Id";
                    cmd.ExecuteNonQuery();
                    //ClearPatientAppointments(appointmentToUpdate.ID, cmd);
                    //FillPatientAppointments(appointmentToUpdate, cmd, appointmentToUpdate.ID);
                    tran.Commit();
                }
            }
        }

        public override void Delete(RadScheduler owner, Appointment appointmentToDelete)
        {
            if (!PersistChanges)
            {
                return;
            }
            using (DbConnection conn = OpenConnection())
            {
                DbCommand cmd = DbFactory.CreateCommand();
                cmd.Connection = conn;
                using (DbTransaction tran = conn.BeginTransaction())
                {
                    cmd.Transaction = tran;
                    ClearPatientAppointments(appointmentToDelete.ID, cmd);
                    cmd.Parameters.Clear();
                    cmd.Parameters.Add(CreateParameter("@Id", appointmentToDelete.ID));
                    cmd.CommandText = "DELETE FROM [PatientAppointment] WHERE [Id] = @Id";
                    cmd.ExecuteNonQuery();
                    tran.Commit();
                }
            }
        }

        public override IEnumerable<ResourceType> GetResourceTypes(RadScheduler owner)
        {
            ResourceType[] resourceTypes = new ResourceType[2];            
            resourceTypes[0] = new ResourceType("Patient", false);
            resourceTypes[1] = new ResourceType("DoctorAppointment", false);
            return resourceTypes;
        }

        public override IEnumerable<Resource> GetResourcesByType(RadScheduler owner, string resourceType)
        {
            LoadResources();
            switch (resourceType)
            {
                case "Patient":
                    return this.Patients.Values;
                case "DoctorAppointment":
                    return this.DoctorAppointments.Values;
                default:
                    throw new InvalidOperationException("Unknown resource type: " + resourceType);
            }
        }

        private void LoadResources(Appointment appointment)
        {
            DatabaseEntities databaseEntities = new DatabaseEntities();

            ObjectQuery<DoctorAppointment> doctorAppointments = databaseEntities.DoctorAppointments.Where("it.ID = @Id");
            doctorAppointments.Parameters.Add(new ObjectParameter("Id", appointment.ID));

            foreach (DoctorAppointment doctorAppointmentItem in doctorAppointments)
            {
                Resource doctorAppointment = DoctorAppointments[doctorAppointmentItem.Id];
                appointment.Resources.Add(doctorAppointment);
            }

            ObjectQuery<PatientAppointment> patientAppointments = databaseEntities.PatientAppointments.Where("it.ID = @Id");
            patientAppointments.Parameters.Add(new ObjectParameter("Id", appointment.ID));

            foreach (PatientAppointment patientAppointment in patientAppointments)
            {
                Resource patient = Patients[patientAppointment.PatientId];
                appointment.Resources.Add(patient);
            }
        }

        private IEnumerable<Resource> LoadDoctorAppointments()
        {
            List<Resource> resources = new List<Resource>();
            DatabaseEntities databaseEntities = new DatabaseEntities();
            foreach (DoctorAppointment doctorAppointment in databaseEntities.DoctorAppointments)
            {
                Resource res = new Resource();
                res.Type = "DoctorAppointment";
                res.Key = doctorAppointment.Id;
                res.Text = string.Format("{0} {1}: {2}\n{3}", doctorAppointment.Doctor.FirstName, doctorAppointment.Doctor.LastName, doctorAppointment.Room.Name, doctorAppointment.Room.Address);
                res.Attributes["DoctorName"] = doctorAppointment.Doctor.FirstName + " " + doctorAppointment.Doctor.LastName;
                res.Attributes["RoomName"] = doctorAppointment.Room.Name + ":" + doctorAppointment.Room.Address;
                resources.Add(res);
            }
            return resources;
        }

        private IEnumerable<Resource> LoadPatients()
        {
            List<Resource> resources = new List<Resource>();
            DatabaseEntities databaseEntities = new DatabaseEntities();
            foreach (Patient patient in databaseEntities.Patients)
            {
                Resource res = new Resource();
                res.Type = "Patient";
                res.Key = patient.Id;
                res.Text = patient.FirstName + " " + patient.LastName;
                res.Attributes["FirstName"] = patient.FirstName;
                res.Attributes["LastName"] = patient.LastName;
                resources.Add(res);
            }
            return resources;
        }

        private void ClearPatientAppointments(object id, DbCommand cmd)
        {
            cmd.Parameters.Clear();
            cmd.Parameters.Add(CreateParameter("@Id", id));
            cmd.CommandText = "DELETE FROM [PatientAppointment] WHERE [Id] = @Id";
            cmd.ExecuteNonQuery();
        }

        private void PopulateAppointmentParameters(DbCommand cmd, Appointment apt)
        {
            cmd.Parameters.Add(CreateParameter("@Subject", apt.Subject));
            cmd.Parameters.Add(CreateParameter("@Start", apt.Start));
            cmd.Parameters.Add(CreateParameter("@End", apt.End));

            Resource doctorAppointment = apt.Resources.GetResourceByType("DoctorAppointment");
            object doctorAppointmentId = doctorAppointment.Key;
            cmd.Parameters.Add(CreateParameter("@DoctorAppointmentId", doctorAppointmentId));

            Resource patient = apt.Resources.GetResourceByType("Patient");
            object patientId = patient.Key;
            cmd.Parameters.Add(CreateParameter("@PatientId", patientId));
        }
    }
}